#include "Room.h"
#include <assert.h>
#include <JsonParse.h>
#include <random>
#include <vector>
#include <Log.h>

Room::~Room() {
    if (_redis) { delete _redis; }
}

// 初始化: 连接Redis服务器, 使用redis++提供的接口
bool Room::initEnvironment() {
    // 格式: tcp://127.0.0.1:6379
    JsonParse json;
    auto info = json.getDataBaseInfo(JsonParse::Redis);
    string connStr = "tcp://" + info->_ip + ":" + to_string(info->_port);
    _redis = new sw::redis::Redis(connStr);
    // 测试连接
    if (_redis->ping() == "PONG") {
        LogDebug("成功连接Redis服务器!");
        return true;
    }
    return false;
}

// 清空数据
void Room::clear() {
    _redis->flushdb();
}

// 保存RSA的密钥对, 1次保存1个: [key -> (field1, value1)、(field2, value2)]
void Room::saveRsaSecKey(string field, string value) {
    _redis->hset("RSA", field, value);
}

// 读取RSA的(公钥/私钥)
string Room::RsaSecKey(string field) {
    auto value = _redis->hget("RSA", field);
    if (value.has_value()) {
        return value.value();
    }
    return string();
}

/* 玩家随机加入1个房间
 *      [1] 优先级: 2人间 > 1人间 > 自己创建房间
 *      [2] scard(): 返回集合(set)的成员数
 *      [3] srandmember(key): 返回[key代指的集合]中的1个随机成员
*/
string Room::joinRoom(string userName) {
    // 找到某个确切的房间
    optional<string> room;
    do {
        if (_redis->scard(_twoPlayer) > 0) { // 有二人间 -> 随机获取1个二人间
            room = _redis->srandmember(_twoPlayer);
            break;
        }
        else if (_redis->scard(_onePlayer) > 0) { // 有一人间 -> 随机获取1个一人间
            room = _redis->srandmember(_onePlayer);
            break;
        }
        room = getNewRoomName(); // 此时没有可进入的房间 -> 创建新房间
    } while (0);

    // 加入到某个房间中
    joinRoom(userName, room.value());
    return room.value();
}

// 玩家加入指定的房间
bool Room::joinRoom(string userName, string roomName) {
    if (_redis->zcard(roomName) >= 3) { // 房间人数≥3
        return false;
    }

    // 检查房间是否存在
    if (!_redis->exists(roomName)) { // 房间不存在 -> 将房间加入[一人房的集合]
        _redis->sadd(_onePlayer, roomName);
    }
    // 更新添加玩家后, 房间的所属类型
    else if (_redis->sismember(_onePlayer, roomName)) { // 当前房间是一人房 -> 将房间从一人房转化为双人房
        _redis->smove(_onePlayer, _twoPlayer, roomName);
    }
    else if (_redis->sismember(_twoPlayer, roomName)) { // 当前房间是二人房 -> 将房间从二人房转化为三人房
        _redis->smove(_twoPlayer, _threePlayer, roomName);
    }
    else { assert(false); } // 异常情况: 直接退出

    // 将玩家添加到房间, 房间使用的结构为sortedset
    _redis->zadd(roomName, userName, 0);

    // 更新玩家的信息: 更新其所属房间
    _redis->hset("Players", userName, roomName);
    return true;
}

// 随机生成房间的名字
string Room::getNewRoomName() {
    // 创建随机设备对象, 用于生成随机数种子
    random_device rd;

    // 创建随机数生成对象
    mt19937 gen(rd());

    // 创建随机数分布对象 -> 均匀分布
    uniform_int_distribution<int> dis(100000, 999999);

    // 生成随机数
    int randNum = dis(gen);
    return to_string(randNum);
}

// 获取房间中玩家的数量
int Room::getPlayerCount(string roomName) {
    int count = _redis->zcard(roomName);
    return count;
}

// 更新玩家的分数
void Room::updatePlayerScore(string roomName, string userName, int score) {
    _redis->zadd(roomName, userName, score);
}

// 通过玩家名找到玩家的所属房间
string Room::whereAmI(string userName) {
    auto room = _redis->hget("Players", userName);
    if (room.has_value()) {
        return room.value();
    }
    return string();
}

// 查询玩家的分数
int Room::playerScore(string roomName, string userName) {
    auto score = _redis->zscore(roomName, userName);
    if (score.has_value()) {
        return static_cast<int>(score.value());
    }
    return 0;
}

// 得到房间内玩家的抢地主次序
string Room::playersOrder(string roomName) {
    int index = 0; // 记录当前次序
    string data;   // 记录结果: 所有玩家 + 抢地主次序, 格式: 用户名-次序-分数#用户名-次序-分数#...
    // 对房间中的玩家进行排序, 降序排序zrevrange
    vector<pair<string, double>> output;
    _redis->zrevrange(roomName, 0, -1, back_inserter(output));
    for (auto& it : output) {
        data += it.first + "-" + to_string(++index) + "-" + to_string((int)it.second) + "#";
    }
    return data;
}

// 用户离开房间
void Room::leaveRoom(string roomName, string userName) {
    // 若该房间尚未被遗弃, 则遗弃该房间: 从三人间转为[被遗弃的房间]
    if (_redis->sismember(_threePlayer, roomName)) { // 该房间属于三人间
        _redis->smove(_threePlayer, _invalid, roomName);
    }

    // 从房间中删除用户: 即用户离开了房间
    _redis->zrem(roomName, userName);

    // 若房间内的用户都被删除了 -> 则删除该房间
    auto count = _redis->zcard(roomName);
    if (count == 0) {
        _redis->del(roomName);
        _redis->srem(_invalid, roomName);
    }
}

// 搜索房间
bool Room::searchRoom(string roomName) {
    // 搜索二人间
    bool flag = _redis->sismember(_twoPlayer, roomName);
    if (!flag) {
        // 搜索一人间
        flag = _redis->sismember(_onePlayer, roomName);
    }
    return flag;
}